I2S (Inter-intergrated Circuit Sound) is a serial standard for sending and receiving PCM audio between digital circuit components. The normal use case is to send audio data to a line-level DAC or amplifier, or to receive audio data from an ADC. I am working on an audio processor which uses a PCM5102 as a DAC output, and a PCM1863 as an ADC. I am using an FPGA as a flexible audio processor.
For this project I am using amaranth-lang which is a python based framework for developing HDL. It allows faster development, and setting up test frameworks easily. There is also quite a lot of work put in to aim amaranth at developing flexible and reusable modules.
To start I am using two streams of data. Each stream has a data port, and a valid and ready port. A stream is valid if data can be pushed out, and ready flags that the sink device can receive that data. In amaranth this can be written as a Signature
class AudioStream(wiring.Signature):
def __init__(self, shape):
super().__init__({
"tdata": Out(shape),
"tvalid": Out(1),
"tready": In(1)
})
Amaranth now provides a stream class, but a custom one with a âtâ prefix helps let Vivado abstract the stream as an AXIS stream. This makes block diagrams cleaner.
Using the stream signature as a port a basic i2s initializator looks like this:
class I2sOut(wiring.Component):
def __init__(self, shape, period = 10, depth = 16, mono = True, ws_switch = 1):
self.period = period # Default period of SCK
self.depth = depth # Width of audio data sent out
self.shape = shape # Shape of stream data, stream data below width is ignored
self.mono = mono # If set to mono, left channel is sent out to both i2s channels, right stream is ignored
self.ws_switch = ws_switch # On which bit to switch WS, 0 is I2S, 1 is left-aligned
super().__init__({
"left": In(AudioStream(shape)),
"right": In(AudioStream(shape)),
"sck": Out(1), # Data clock
"bck": Out(1), # PLL clock
"ws": Out(1), # Word select (left/right)
"sd": Out(1) # Data out
})
Now for implementation. To start we need a BCK clock out.